/**
 * @author Ashish Tomar
 * The module provides functions to get Stock Quote from Yahoo Finance.
 * Converted into Scala from a Perl Module YahooQuote.pm which was developed by Dj Padzensky and Dirk Eddelbuette
 * 
 */

package Finance
import scalaj.http.Http




class YahooQuote {

	// These variables govern what type of quote the modules is retrieving
	private var QURLBase = "http://download.finance.yahoo.com/d/quotes.csvr?e=.csv&f=";
	private var QURLFormat = "snl1d1t1c1p2va2bapomwerr1dyj1x";	// default up to 0.19
	private val QURLExtended = "s7t8e7e8e9r6r7r5b4p6p5j4m3m4";	// new in 0.20
	private val QURLRealtime = "b2b3k2k1c6m2j3";//  also new in 0.20
	private val QURLEnd = "&s=";
	private var QURL = QURLBase + QURLFormat +QURLEnd;//  define old format as default

	//HashMap Containing the mapping of the field name to its character for the format string 
	private  val fields = scala.collection.immutable.HashMap[String,String](
			"Symbol"				->  "s",	// old default
			"Name"				->  "n",	// old default
			"Last Trade (With Time)"		->  "l",
			"Last Trade (Price Only)"		->  "l1",	// old default
			"Last Trade Date"			->  "d1",	// old default
			"Last Trade Time"			->  "t1",	// old default
			"Last Trade Size"			->  "k3",
			"Change and Percent Change"	->  "c",
			"Change"				->  "c1",	// old default
			"Change in Percent"		->  "p2",	// old default
			"Ticker Trend"			->  "t7",
			"Volume"				->  "v",	// old default
			"Average Daily Volume"		->  "a2",	// old default
			"More Info"			->  "i",
			"Trade Links"			->  "t6",
			"Bid"				->  "b",	// old default
			"Bid Size"			->  "b6",
			"Ask"				->  "a",	// old default
			"Ask Size"			->  "a5",
			"Previous Close"			->  "p",	// old default
			"Open"				->  "o",	// old default
			"Day's Range"			->  "m",	// old default
			"52-week Range"			->  "w",	// old default
			"Change From 52-wk Low"		->  "j5",
			"Pct Chg From 52-wk Low"		->  "j6",
			"Change From 52-wk High"		->  "k4",
			"Pct Chg From 52-wk High"		->  "k5",
			"Earnings/Share"			->  "e",	// old default
			"P/E Ratio"			->  "r",	// old default
			"Short Ratio"			->  "s7",
			"Dividend Pay Date"		->  "r1",	// old default
			"Ex-Dividend Date"		->  "q",
			"Dividend/Share"			->  "d",	// old default
			"Dividend Yield"			->  "y",	// old default
			"Float Shares"			->  "f6",
			"Market Capitalization"		->  "j1",	// old default
			"1yr Target Price"		->  "t8",
			"EPS Est. Current Yr"		->  "e7",
			"EPS Est. Next Year"		->  "e8",
			"EPS Est. Next Quarter"		->  "e9",
			"Price/EPS Est. Current Yr"	->  "r6",
			"Price/EPS Est. Next Yr"		->  "r7",
			"PEG Ratio"			->  "r5",
			"Book Value"			->  "b4",
			"Price/Book"			->  "p6",
			"Price/Sales"			->  "p5",
			"EBITDA"				->  "j4",
			"50-day Moving Avg"		->  "m3",
			"Change From 50-day Moving Avg"	->  "m7",
			"Pct Chg From 50-day Moving Avg"	->  "m8",
			"200-day Moving Avg"		->  "m4",
			"Change From 200-day Moving Avg"	->  "m5",
			"Pct Chg From 200-day Moving Avg"	->  "m6",
			"Shares Owned"			->  "s1",
			"Price Paid"			->  "p1",
			"Commission"			->  "c3",
			"Holdings Value"			->  "v1",
			"Day's Value Change"		->  "w1",
			"Holdings Gain Percent"		->  "g1",
			"Holdings Gain"			->  "g4",
			"Trade Date"			->  "d2",
			"Annualized Gain"			->  "g3",
			"High Limit"			->  "l2",
			"Low Limit"			->  "l3",
			"Notes"				->  "n4",
			"Last Trade (Real-time) with Time"->  "k1",
			"Bid (Real-time)"			->  "b3",
			"Ask (Real-time)"			->  "b2",
			"Change Percent (Real-time)"	->  "k2",
			"Change (Real-time)"		->  "c6",
			"Holdings Value (Real-time)"	->  "v7",
			"Day's Value Change (Real-time)"	->  "w4",
			"Holdings Gain Pct (Real-time)"	->  "g5",
			"Holdings Gain (Real-time)"	->  "g6",
			"Day's Range (Real-time)"		->  "m2",
			"Market Cap (Real-time)"		->  "j3",
			"P/E (Real-time)"			->  "r2",
			"After Hours Change (Real-time)"	->  "c8",
			"Order Book (Real-time)"		->  "i5",
			"Stock Exchange"			->  "x");


	/**
	 *  Simple function to switch to extended format
	 */
	def useExtendedQueryFormat {
		if(!QURLFormat.contains(QURLExtended)){
			QURLFormat += QURLExtended;
		}
	}

	/**
	 *  Simple function to append real-time format
	 */
	def useRealtimeQueryFormat {
		if(!QURLFormat.contains(QURLRealtime)){
			QURLFormat += QURLRealtime;
		}
	}

	/**
	 *  Allow module user to define a new query string
	 */
	def setQueryString(format:String) {

		QURLFormat = format;
	}

	/**
	 *  Allow module user to set the format back to default
	 */
	def setQueryStringBackToDefault() {

		QURLFormat = "snl1d1t1c1p2va2bapomwerr1dyj1x";
	}

	/**
	 *  Allow module user to define a new server url
	 */
	def setURLServerString(server:String,urlWoParameters:Boolean) {
		if(urlWoParameters){
			QURLBase = server+"/d/quotes.csvr?e=.csv&f="
		}
		else{
			QURLBase = server
		}
	}

	/**
	 * Gets the quote values for the list of symbols passed as an argument. The format string used is QURLFormat.
	 * Returns a List of Arrays.
	 */
	def getQuote(symbols:List[String]) : List[Array[String]] = {

			//println("Yahoo Finance Caller : getQuote")

			readYahoo(symbols,QURLFormat);// Old default from variable

	}

	/**
	 * Gets the quote value for a symbol passed as an argument. The format string used is QURLFormat.
	 * Returns a List of Array.
	 */
	def getOneQuote(symbol:String) : List[Array[String]] = {

			//println("Yahoo Finance Caller : getOneQuote")

			readYahoo(List(symbol),QURLFormat);// Old default from variable

	}


	/**
	 * Gets the quote value for the list of symbols passed as an argument.
	 * Let the user define which columns to retrieve from Yahoo.
	 * Returns a List of Array.
	 */
	def getCustomQuote(symbols:List[String], columns:List[String]) : List[Array[String]] =  {

			//println("Yahoo Finance Caller : getCustomQuote")

			readYahoo(symbols,getFormatStringFromList(columns));


	}

	/**
	 * Common function for reading the yahoo site and retrieving and
	 * parsing the data returned. The list of symbols is supplied as 
	 * first argument and format used is supplied as a second argument, 
	 * the default function getQuote() uses the default format QURLFormat, and either one of
	 * the two preceding functions can be used to select the extended format.
	 * Returns a list of Arrays.
	 */
	private  def readYahoo(symbols:List[String],format:String) : List[Array[String]] = {


			var localSymbols:List[String] = symbols

			var listOfArray:List[Array[String]] = List[Array[String]]()


			//Loop over the list of symbols, grabbing 199 symbols at a 
			//time, since yahoo only lets you get 200 per request

			try{
				while (localSymbols.length != 0 ) {


					val b = getSymbolsInProperCount(localSymbols)
					localSymbols = trimSymbolsInProperCount(localSymbols)

					val url = QURLBase + format + QURLEnd + getSymbolsFromList(b)
					//println(url)

					val httpResult:String = Http(url).asString
					val arrOfLines= httpResult.split('\n')

					for(ind <- 0 until arrOfLines.length){
						val arrLine:Array[String]= arrOfLines(ind).split(',')

						listOfArray = listOfArray :+ arrLine

					}

				}
			}
	catch{
	case ex : java.net.SocketTimeoutException => println("Socket Timeout. Please Try Again. ");
	}

	listOfArray


	}

	/**
	 * Takes a list of Strings as Input.
	 * Returns a single string formed from the list of strings concatenated by "+" sign
	 */
	private def getSymbolsFromList(symbols:List[String]) : String = {

			symbols.mkString("+")

	}

	/**
	 * Helper function to Loop over the list of symbols, grabbing 200 symbols at a time,
	 * since yahoo only lets you get 200 per request.
	 * The function returns the first 200 symbols as a list or the whole list
	 * if the size of the list is less than 200.
	 */
	private def getSymbolsInProperCount(symbols:List[String]) : List[String] = {
			if(symbols.length >= 200 ){	
				symbols.slice(0,200);  
			}
			else{
				symbols.slice(0,symbols.length);
			}
	}

	/**
	 * Helper function to Loop over the list of symbols, grabbing 200 symbols at a time,
	 * since yahoo only lets you get 200 per request. 
	 * The function returns a list after removing the first 200 symbols or empty list
	 * if the size of the list is less than 200.
	 */
	private def trimSymbolsInProperCount(symbols:List[String]) : List[String] = {
			if(symbols.length >= 200 ){	
				symbols.slice(200,symbols.length);
			}
			else{
				List[String]();

			}
	}

	/**
	 * Gets the character value from the fields HashMap for the list of column names passed as an argument.
	 * Return a String of concatenated character values from the map to be used as a new format for getting the quote.
	 */
	private def getFormatStringFromList(columns:List[String]) : String = {

			var formatString:String = ""
				columns.foreach(columnName => {

					//println(fields(columnName))
					formatString += fields(columnName)

				})
				//println(formatString)
				formatString
	}


	/**
	 * Prints a List of Array and all the Array elements one by one.
	 */
	def printListOfArray(varListOfArray:List[Array[String]]){
		//var cnt:Int=0
		varListOfArray.foreach(arr => {
			//cnt+=1
			//print(cnt+" "+ arr.mkString(" "))
			print(arr.mkString(" "))
		})
	}

}